#ifndef BITINPUT_HPP
#define BITINPUT_HPP 1

#include <iostream>
#include <cstdint>
#include <fstream>
#include "exception.hpp"

namespace fur
{
   namespace
   {
      const size_t default_bitinput_buffsize = 0x8000;
   }
   class BitInputBase
   {
   public:
      inline uint16_t getbits_8() const throw();
      inline uint16_t getbits_16() const throw();
      inline void addbits(uint32_t bits) throw();
      void reset() throw();
      void align() throw();
      inline signed int avail_size() const throw();
      BitInputBase(uint8_t *array, size_t buffsize) throw(Exception);
      ~BitInputBase() throw();
      
   protected:
      BitInputBase(size_t buffsize = default_bitinput_buffsize) throw(Exception);
      size_t size;
      size_t maxsize;
      int bitposi;
      size_t byteposi;
      uint8_t *data;
   };
   
   class BitInput:public BitInputBase
   {
   public:
      BitInput(std::istream &file, size_t buffsize = default_bitinput_buffsize) throw(Exception);
      bool read() throw();
      bool eof() throw();
      ~BitInput() throw();
   private:
      std::istream &is;
   };
   
   /* inline functions */
   uint16_t BitInputBase::getbits_16() const throw()
   {
      uint32_t bitfield = data[byteposi] << 16;
      bitfield |= data[byteposi + 1] << 8;
      bitfield |= data[byteposi + 2];
      bitfield >>= 8 - bitposi;
      return bitfield;
   }

   uint16_t BitInputBase::getbits_8() const throw()
   {
      uint16_t bitfield = data[byteposi] << 8;
      bitfield |= data[byteposi + 1];
      bitfield >>= 8 - bitposi;
      bitfield &= 0xff;
      return bitfield;
   }

   void BitInputBase::addbits(uint32_t bits) throw()
   {
      bits += bitposi;
      byteposi += bits >> 3;
      bitposi = bits & 7;
   }

   signed int BitInputBase::avail_size() const throw()
   {
      return size - byteposi;
   }
} // end of namespace fur


#endif /* bitinput.hpp */
